<template>
  <div class="md-file-upload-base">
    <el-upload :headers="headers" :data="params" :disabled="disabled" :action="action" :show-file-list="false" :on-success="handleSuccess" :before-upload="handleBefore">
      <el-button icon="el-icon-upload" size="small">
        <span v-if="!part">{{text}}</span>

        <span v-if="part && partProgress===0">{{text}}</span>
        <span v-if="part && partProgress>0 && partProgress<100">上传中{{parseFloat(partProgress).toFixed(2)}}%</span>
        <span v-if="part && partProgress===100">{{text}}</span>
      </el-button>
    </el-upload>

    <div style="font-size: 12px; color: #f00;" v-if="showDesc">
      <p>请上传 {{exts.join(',')}} 格式文件！文件大小 {{size}}{{sizeUnit}}！{{desc}}</p>
    </div>
  </div>
</template>

<script>
import { mdApiBase } from '@/ddd/config.service.js'
import { _dataEncryption } from '@/ddd/encryption.service.js'
import sparkMD5 from 'spark-md5'
export default {
  name: 'MdButtonFileUploadBaseComponent',
  props: {
    value: [String],
    /** 请求参数 */
    params: {
      type: Object,
      default: () => {
        return {}
      }
    },
    /** 请求地址 */
    action: {
      type: String,
      default: `${mdApiBase}/public/upload/tencent/single`
    },
    /** 缺省文本信息 */
    text: {
      type: String,
      default: '上传文件'
    },
    /** 文件扩展名 */
    exts: {
      type: Array,
      default: () => ['mp4', 'mp3', 'xls']
    },
    /** 文件大小 */
    size: {
      type: Number,
      default: 10
    },
    /** 文件大小单位 */
    sizeUnit: {
      type: String,
      default: 'MB'
    },
    /** 描述信息：如图片尺寸描述等 */
    desc: {
      type: String,
      default: null
    },
    /** 是否分片上传 */
    part: {
      type: Boolean,
      default: false
    },
    /** 是否显示描述信息 */
    showDesc: {
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      val: this.value,
      headers: {
        Authorization: `Bearer ${this.$local.getItem('Authorization')}`
      },
      disabled: false,
      partProgress: 0, // 分片进度
      partList: [], // 分片集合
      loading: null
    }
  },
  watch: {
    value: {
      handler (val) {
        this.handleSetValue(val)
      },
      deep: true
    }
  },
  mounted () {
    this.handleSetValue(this.value)
  },
  methods: {
    /** 设置值 */
    handleSetValue (val) {
      this.$nextTick(() => {
        const model = {}
        const signModel = _dataEncryption(model)
        this.params.merchantId = signModel.merchantId
        this.params.ip = signModel.ip
        this.params.mac = signModel.mac
        this.params.isValid = signModel.isValid
        this.params.version = signModel.version
        this.params.timesTamp = signModel.timesTamp
        this.params.token = signModel.token
        this.params.sign = signModel.sign

        this.val = val
      })
    },
    /** 上传前验证 */
    handleBefore (file) {
      this.loading = this.$loading({ text: this.part ? '队列中...' : '上传中...' })

      const isMB = file.size / 1024 / 1024 < this.size;

      if (!isMB) {
        this.$message.error(`上传图片大小不能超过 ${this.size}${this.sizeUnit}!`)
        if (this.loading) this.loading.close()
        return false
      }

      const isExt = this.exts.includes((file.name.split('.').pop()).toLowerCase())

      if (!isExt) {
        this.$message.error(`上传图片格式：${this.exts.join(',')}!`)
        if (this.loading) this.loading.close()
        return false
      }

      if (this.part) {
        this.disabled = true
        this.partProgress = 0
        this.partList = []

        this.handleImagePartUpload(file)
      }

      return this.part ? false : true
    },
    /** 文件分片上传 */
    async handleImagePartUpload (file) {
      const ext = file.name.split('.').pop()
      const name = file.name.replace(`.${ext}`, '')
      const size = file.size
      const buffer = await this.fileParse(file, 'buffer')

      const spark = new sparkMD5.ArrayBuffer()
      spark.append(buffer)
      const fileMD5 = spark.end()

      const chunkSize = 1 * 1024 * 1024 // 1MB一片
      const chunkCount = Math.ceil(file.size / chunkSize) // 总片数

      for (let index = 0; index < chunkCount; index++) {
        const start = index * chunkSize
        const end = Math.min(file.size, start + chunkSize)
        const chunk = file.slice(start, end)

        const chunkBuffer = await this.fileParse(chunk, 'buffer')
        const chunkSpark = new sparkMD5.ArrayBuffer()
        chunkSpark.append(chunkBuffer)

        const chunkMD5 = chunkSpark.end()

        const item = {
          index: index,
          name: name,
          ext: ext,
          size: size,
          fileMD5: fileMD5,
          chunkName: `${fileMD5}_${chunkMD5}_${index}`,
          chunk: chunk, // 分片文件
          chunkMD5: chunkMD5, // 分片文件md5
          chunkSize: chunkSize, // 分片文件大小
          chunkCount: chunkCount, // 分片总数
          uid: file.uid,
          chunkStart: 0, // 当前片
          chunkEnd: chunkCount - index - 1 // 剩余片
        }

        this.partList.push(item)
      }

      if (this.loading) this.loading.close()

      for (let index = 0; index < this.partList.length; index++) {
        const item = this.partList[index]

        const fd = new FormData()

        fd.append('index', item.index)
        fd.append('name', item.name)
        fd.append('ext', item.ext)
        fd.append('size', item.size)
        fd.append('fileMD5', item.fileMD5)
        fd.append('chunkName', item.chunkName)
        fd.append('chunk', item.chunk)
        fd.append('chunkMD5', item.chunkMD5)
        fd.append('chunkSize', item.chunkSize)
        fd.append('chunkCount', item.chunkCount)
        fd.append('uid', item.uid)
        fd.append('chunkStart', item.chunkStart)
        fd.append('chunkEnd', item.chunkEnd)

        const res = await this.$http.post('/util/upload/multipart', fd, { headers: this.headers, showLoading: false })

        this.partProgress += parseFloat((100 / item.chunkCount) * 100) / 100

        if (this.partProgress > 100) {
          this.partProgress = 100
        }

        if (res.msg === 'merge success') {
          this.partProgress = 100

          this.val = res.data.fileUrl
          this.$emit('input', this.val)
          this.$emit('change', { val: this.val, res: res.data })
        }
      }

      this.disabled = false
    },
    /**
     * 文件转换
     * @param {*} file 文件
     * @param {*} type 转换类型 base64、buffer
     */
    fileParse (file, type = 'base64') {
      return new Promise(resolve => {
        let fileRead = new FileReader();
        if (type === 'base64') {
          fileRead.readAsDataURL(file);
        } else if (type === 'buffer') {
          fileRead.readAsArrayBuffer(file);
        }
        fileRead.onload = (ev) => {
          resolve(ev.target.result);
        }
      })
    },
    /** 上传成功回调 */
    async handleSuccess (res) {
      if (res.code === '200') {
        this.val = res.data.yunFilePath
        this.$emit('input', this.val)
        this.$emit('change', { val: this.val, res: res.data })
      }

      if (this.loading) this.loading.close()
    }
  }
}
</script>

<style lang="less" scoped>
.md-file-upload-base {
  display: inline-block;
  .el-button {
    background-color: @md-primary-color;
    color: #fff;
    border-radius: 0px !important;
  }
}
</style>